package com.gandong8.trace.web.core;


import com.gandong8.trace.tool.core.SystemClock;
import com.gandong8.trace.tool.util.JsonUtil;
import com.gandong8.trace.tool.util.TraceUtil;
import com.gandong8.trace.web.domain.common.RequestLog;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

@Aspect
@Component
@Order(1)
public class ControllerAspect {
    @Resource(name="webJackson")
    ObjectMapper objectMapper;

    @Pointcut("execution(public * com.gandong8.trace.web.controller.*.*(..))")
    public void requestLog() {
    }

    @Before("requestLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e 异常
     */
    @AfterThrowing(value = "requestLog()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e)
    {
        e.printStackTrace();
        System.out.println("aaa");
        //handleLog(joinPoint, e, null);
    }


    /**
     *  处理完请求后执行
     * @param ret
     * @throws Throwable
     */
    @AfterReturning(value = "requestLog()", returning = "ret")
    public void doAfterReturning(Object ret) throws Throwable {
    }

    @Around("requestLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        RequestLog log = new RequestLog();
        log.setStartTime(SystemClock.now());
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        log.setMethod(request.getMethod());
        log.setPath(request.getServletPath());
        log.setClientIp(getClientIp(request));
        log.setContainer(getInnerIp());
        //log.setHeader(getHeadersInfo(request));
        //log.setCookie(getCookieInfo(request));
        Object result = joinPoint.proceed();
        log.setEndTime(SystemClock.now());
        log.setCostTime(log.getEndTime() - log.getStartTime());
        log.setGetData(request.getQueryString());
        if("POST".equals(log.getMethod())) {
            //RequestBody RequestParam method.getParameters()
            Object[] args = joinPoint.getArgs();
            if(args.length > 0) {
                log.setPostData(JsonUtil.toStr(args[0]));
            }
        }
        log.setResult(result);
        log.setTraceId(TraceUtil.getTraceId());
        //System.out.println(log);
        return result;
    }

    private String getInnerIp() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            return null;
        }
    }



    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if(StringUtils.isNotEmpty(ip) && !"unknown".equals(ip)) {
            return ip;
        }
        ip = request.getHeader("Proxy-Client-IP");
        if(StringUtils.isNotEmpty(ip) && !"unknown".equals(ip)) {
            return ip;
        }
        ip = request.getHeader("WL-Proxy-Client-IP");
        if(StringUtils.isNotEmpty(ip) && !"unknown".equals(ip)) {
            return ip;
        }
        ip = request.getHeader("HTTP_CLIENT_IP");
        if(StringUtils.isNotEmpty(ip) && !"unknown".equals(ip)) {
            return ip;
        }
        ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        if(StringUtils.isNotEmpty(ip) && !"unknown".equals(ip)) {
            return ip;
        }
        return request.getRemoteAddr();
    }

    private String getCookieInfo(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if(cookies == null || cookies.length < 1) {
            return null;
        }
        StringBuilder result = new StringBuilder();
        for (Cookie cookie : cookies){
            result.append(cookie.getName() ).append("=").append(cookie.getValue()).append(";");
        }
        return result.toString();
    }
    private static Map<String, Integer> headerNotMatch = new HashMap<>();
    {
        headerNotMatch.put("cookie", 1);
        headerNotMatch.put("cache-control", 1);
        headerNotMatch.put("accept", 1);
        headerNotMatch.put("connection", 1);
        headerNotMatch.put("accept-encoding", 1);
        headerNotMatch.put("accept-language", 1);
        headerNotMatch.put("origin", 1);
        headerNotMatch.put("content-type", 1);
        headerNotMatch.put("content-length", 1);
    };

    private String getHeadersInfo(HttpServletRequest request) {
        Map<String, String> map = new HashMap<>();
        Enumeration headerNames = request.getHeaderNames();
        StringBuilder result = new StringBuilder();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            String value = request.getHeader(key);
            String tmp = key.toLowerCase();
            if(headerNotMatch.containsKey(tmp)) {
                continue;
            }
            result.append(key).append("=").append(map.get(key)).append(";");
            map.put(key, value);
        }
        return result.toString();
    }

}
